#include "PreCompile.hpp"
#include "InputFilesSet.hpp"
#include "InputFiles.hpp"
#include "LibBuildNGInit.hpp"

namespace BuildNG
{
  ////////////
  // InputFilesSet
  InputFilesSet::InputFilesSet()
  {
    assert_libbuildng_initialized();

    root_group = Glib::RefPtr<Group>(new Group);
  }

  InputFilesSet::~InputFilesSet()
  {
  }

  Glib::RefPtr<InputFilesSet> InputFilesSet::create()
  {
    return Glib::RefPtr<InputFilesSet>(new InputFilesSet);
  }

  void InputFilesSet::clear()
  {
    root_group->clear();
  }

  Glib::RefPtr<InputFilesSet::Group> InputFilesSet::add_group()
  {
    return root_group->add_child_group();
  }

  Glib::RefPtr<InputFilesSet::Group> InputFilesSet::add_group_from_xml(const Xml::Element &xml)
  {
    return root_group->add_child_group_from_xml(xml);
  }

  int InputFilesSet::get_num_input_files_entries() const
  {
    return root_group->get_num_input_files_entries();
  }

  Glib::RefPtr<InputFiles> InputFilesSet::get_input_files_entry(int i) const
  { 
    assert(!"not implemented yet");
    throw 5;
    //return entries[i];
  }

  std::vector<Glib::ustring> InputFilesSet::build_physical_files(BuildContext &context)
  {
    assert(!"not implemented yet");
    throw 5;

    /*std::vector<Glib::ustring> ret;
    for(int i = 0;i < (int)entries.size();i++)
    {
      Glib::RefPtr<InputFiles> entry = entries[i];

      std::vector<Glib::ustring> cur_phys_files = 
        entry->build_physical_files(context);
      for(int j = 0;j < (int)cur_phys_files.size();j++)
        ret.push_back(cur_phys_files[j]);
    }
    return ret;*/
  }

  InputFilesSet::Iterator InputFilesSet::get_input_files_entry_iterator()
  {
    Iterator ret(this_ptr<InputFilesSet>());
    return ret;
  }

  /////////////
  // InputFilesSet::Group
  InputFilesSet::Group::Group()
  {
  }

  InputFilesSet::Group::~Group()
  {
  }

  InputFilesSet::Group::Group(const Xml::Element &xml)
  {
    Xml::ConstElementList children = xml.get_child_elements();
    for(int i = 0;i < (int)children.size();i++)
    {
      const Xml::Element *cur_elem = children[i];
      assert(cur_elem);

      if(cur_elem->get_name() == "group")
      {
        add_child_group_from_xml(*cur_elem);
      }
      else
      {        
        Glib::RefPtr<InputFilesTypesRegistry::TypeInfo> type_info = 
          input_files_types_registry.get_by_xml_tag_name(cur_elem->get_name());
        if(!type_info)
        {
          throw Xml::ContentException(xml,"Unknown input file type: \"" + cur_elem->get_name() + "\".");
        }

        Glib::RefPtr<InputFiles> cur_input_files = type_info->create_instance();
        cur_input_files->load_from_xml(*cur_elem);
        entries.push_back(cur_input_files);
      }
    }
  }

  int InputFilesSet::Group::get_num_input_files_entries() const
  {
    int ret = (int)entries.size();
    for(int i = 0;i < (int)child_groups.size();i++)
      ret += child_groups[i]->get_num_input_files_entries();
    return ret;
  }

  Glib::RefPtr<InputFilesSet::Group> InputFilesSet::Group::add_child_group()
  {
    Glib::RefPtr<Group> child(new Group);
    child_groups.push_back(child);
    return child;
  }

  Glib::RefPtr<InputFilesSet::Group> InputFilesSet::Group::add_child_group_from_xml(const Xml::Element &xml)
  {
    Glib::RefPtr<Group> child(new Group(xml));
    child_groups.push_back(child);
    return child;
  }

  void InputFilesSet::Group::clear()
  {
    entries.clear();
    child_groups.clear();
  }

  ////////////
  // InputFilesSet::Iterator
  InputFilesSet::Iterator::Iterator(Glib::RefPtr<InputFilesSet> files_set)
  {
    this->files_set = files_set;
    index = -1;

    RecursionStackEntry new_stack_entry;
    new_stack_entry.group = files_set->root_group;
    new_stack_entry.index_in_parent = -1;
    recursion_stack.append(new_stack_entry);
  }

  bool InputFilesSet::Iterator::move_next()
  {
    while(recursion_stack.get_size())
    {
      index++;

      const RecursionStackEntry &cur_stack_entry = recursion_stack[recursion_stack.get_size() - 1];

      Glib::RefPtr<Group> cur_group = cur_stack_entry.group;
      assert(index >= 0);
      if(index < (int)cur_group->entries.size())
      {
        cur = cur_group->entries[index];
        return true;
      }
      else if(index - cur_group->entries.size() < cur_group->child_groups.size())
      {
        cur_group = cur_group->child_groups[index - cur_group->entries.size()];

        RecursionStackEntry new_stack_entry;
        new_stack_entry.group = cur_group;
        new_stack_entry.index_in_parent = index;
        recursion_stack.append(new_stack_entry);

        index = -1;
      }
      else
      {      
        index = cur_stack_entry.index_in_parent;
        recursion_stack.remove(recursion_stack.get_size() - 1);
      }
    }

    cur.clear();
    return false;
  }

  Glib::RefPtr<InputFiles> InputFilesSet::Iterator::get_entry()
  {
    return cur;
  }
}

